package com.lab.es.demo.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lab.es.demo.annotation.IndexHandle;
import com.lab.es.demo.common.ResFactory;
import com.lab.es.demo.common.ResPaging;
import com.lab.es.demo.dto.SearchItemDTO;
import com.lab.es.demo.dto.UpdateItemDTO;
import com.lab.es.demo.entity.Item;
import com.lab.es.demo.mapper.ItemMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ItemService {
    private static final String INDEX_NAME = "tmall_items";
    @Autowired
    private ItemMapper itemMapper;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    public Boolean databaseToES() throws Exception {
        QueryWrapper<Item> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id", "item_name", "master_pic", "item_price", "tbk_link", "shop_name", "monthly_sales");

        List<Item> items = itemMapper.selectList(queryWrapper);

        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");

        Item item = null;
        for (int i = 0; i < items.size(); i++) {
            item = items.get(i);
            bulkRequest.add(
                    new IndexRequest(INDEX_NAME).id(item.getId()).source(JSON.toJSONString(item), XContentType.JSON)
            );
        }

        BulkResponse response = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !response.hasFailures();
    }

    public ResPaging searchPageHighlight(SearchItemDTO searchItemDTO) throws IOException {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.from((searchItemDTO.getPageNo() - 1) * searchItemDTO.getPageSize());
        sourceBuilder.size(searchItemDTO.getPageSize());

        MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = QueryBuilders.matchPhrasePrefixQuery("itemName", searchItemDTO.getKeyword());

        sourceBuilder.query(matchPhrasePrefixQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        String sort = searchItemDTO.getSort();
        if (sort.equals("itemPriceUp")) {
            sourceBuilder.sort(new FieldSortBuilder("itemPrice").order(SortOrder.ASC));
        } else if (sort.equals("itemPriceDown")) {
            sourceBuilder.sort(new FieldSortBuilder("itemPrice").order(SortOrder.DESC));
        } else if (sort.equals("monthlySalesUp")) {
            sourceBuilder.sort(new FieldSortBuilder("monthlySales").order(SortOrder.ASC));
        } else if (sort.equals("monthlySalesDown")) {
            sourceBuilder.sort(new FieldSortBuilder("monthlySales").order(SortOrder.DESC));
        }

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("itemName");
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red;'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);

        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        List<Map<String, Object>> list = new ArrayList<>();
        SearchHits searchHits = searchResponse.getHits();
        int totalHits = (int) searchHits.getTotalHits().value;

        for (SearchHit hit : searchHits.getHits()) {
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField itemName = highlightFields.get("itemName");
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            if (itemName != null) {
                Text[] fragments = itemName.fragments();
                String n_itemName = "";
                for (Text text : fragments) {
                    n_itemName += text;
                }
                sourceAsMap.put("itemNameHighlight", n_itemName);
            }
            list.add(sourceAsMap);
        }

        return ResFactory.getInstance().getResPaging(list, searchItemDTO.getPageNo(), searchItemDTO.getPageSize(), totalHits);
    }

    @IndexHandle(desc = "异步更新ElasticSearch索引")
    public int updateItem(UpdateItemDTO updateItemDTO) throws Exception {
        if (StringUtils.isEmpty(updateItemDTO.getId())) {
            throw new Exception("更新商品，ItemId 不能为空");
        }

        try {
            Item item = itemMapper.selectById(updateItemDTO.getId());
            item.setItemName(updateItemDTO.getItemName());
            item.setItemPrice(updateItemDTO.getItemPrice());
            item.setMonthlySales(updateItemDTO.getMonthlySales());
            return itemMapper.updateById(item);
        } catch (Exception e) {
            throw new Exception("更新商品发生错误，" + e.getMessage());
        }
    }
}
